package com.eclink.asset.workflow.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.generator.SnowflakeGenerator;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.eclink.asset.common.domain.dto.CommonUserDTO;
import com.eclink.asset.common.exception.BusinessException;
import com.eclink.asset.domain.AuditProcess;
import com.eclink.asset.domain.AuditConfigNode;
import com.eclink.asset.asset.domain.dao.FlowApply;
import com.eclink.asset.workflow.domain.dao.*;
import com.eclink.asset.workflow.domain.dto.DoTaskDTO;
import com.eclink.asset.enums.ApplyStateEnum;
import com.eclink.asset.enums.FlowStateEnum;
import com.eclink.asset.enums.FlowTaskStateEnum;
import com.eclink.asset.setup.domain.dao.Company;
import com.eclink.asset.sys.domain.dto.SysUserDetailDTO;
import com.eclink.asset.sys.service.SysUserService;
import com.eclink.asset.workflow.enums.AuditProcessStatusEnum;
import com.eclink.asset.workflow.enums.FlowTaskAuditUserApproveFlagEnum;
import com.eclink.asset.workflow.enums.FlowTaskTypeEnum;
import com.eclink.asset.workflow.event.ApplyApproveEvent;
import com.eclink.asset.mapper.FlowMapper;
import com.eclink.asset.mapper.FlowTaskMapper;
import com.eclink.asset.workflow.event.ApplyRejectEvent;
import com.eclink.asset.workflow.mapper.FlowApplyMapper;
import com.eclink.asset.workflow.mapper.FlowTaskApproveMapper;
import com.eclink.asset.workflow.mapper.FlowTaskAuditUserMapper;
import com.eclink.asset.workflow.mapper.TodoMapper;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Service
public class FlowService {

    @Resource
    private ApplicationEventPublisher eventPublisher;

    @Resource
    private AuditConfigService auditConfigService;

    @Resource
    private SnowflakeGenerator idGenerator;

    @Resource
    private FlowMapper flowMapper;

    @Resource
    private FlowTaskMapper flowTaskMapper;

    @Resource
    private TodoMapper todoMapper;

    @Resource
    private FlowApplyMapper flowApplyMapper;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private FlowTaskAuditUserMapper flowTaskAuditUserMapper;

    @Resource
    private FlowTaskApproveMapper flowTaskApproveMapper;

    private FlowTask createTask(AuditConfigNode node, String flowId, FlowApply flowApply, Integer todoType) {

        String taskId = idGenerator.next()+"";
        FlowTask flowTask = new FlowTask();
        flowTask.setName(node.getName());
        flowTask.setStartTime(LocalDateTime.now());
        flowTask.setFlowId(flowId);
        flowTask.setApplyId(flowApply.getId());
        flowTask.setApplyNumber(flowApply.getNumber());
        flowTask.setApplyType(flowApply.getApplyType());
        flowTask.setApplyTime(flowApply.getCreateTime());
        flowTask.setAuditRule(node.getAuditRule());
        flowTask.setTodoType(todoType);
        flowTask.setId(taskId);

        List<CommonUserDTO> userList = node.getAuditUserList();
        flowTask.setAuditUser(createFlowTaskAuditUserList(userList, taskId));

        return flowTask;
    }

    public void clearTaskByFlowId(String flowId) {
        LambdaQueryWrapper<FlowTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FlowTask::getFlowId, flowId);
        flowTaskMapper.delete(queryWrapper);
    }

    /**
     * 清除掉申请旧的工作流信息
     * @param applyId 申请ID
     */
    private void clearFlowByApplyId(String applyId) {
        LambdaQueryWrapper<Flow> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Flow::getApplyId, applyId);
        List<Flow> oldFlowList = flowMapper.selectList(queryWrapper);
        for(Flow flow : oldFlowList) {
            String flowId = flow.getId();
            flowMapper.deleteById(flowId);
            clearTaskByFlowId(flowId);
        }
    }

    /**
     * 把用户信息转成JSON格式
     * @param userDTO dto
     * @return
     */
    private String toJSONArrayStr(CommonUserDTO userDTO) {
        List<CommonUserDTO> list = new ArrayList<>();
        list.add(userDTO);
        return JSONArray.toJSONString(list);
    }

    /**
     * 把用户信息转成JSON格式
     * @param str str
     * @return 结果
     */
    private List<CommonUserDTO> parseAuditUser(String str) {
        if (str == null || str.trim().isEmpty()) {
            return null;
        }
        return JSONArray.parseArray(str, CommonUserDTO.class);
    }

    private List<FlowTaskAuditUser> createFlowTaskAuditUserList(List<CommonUserDTO> userList, String taskId) {
        List<FlowTaskAuditUser> res = new ArrayList<>();
        for(CommonUserDTO dto : userList) {
            res.add(createFlowTaskAuditUser(dto, taskId));
        }
        return res;
    }

    private FlowTaskAuditUser createFlowTaskAuditUser(CommonUserDTO user, String taskId) {
        FlowTaskAuditUser u = new FlowTaskAuditUser();
        u.setUserId(user.getUserId());
        u.setUserName(user.getNickName());
        u.setAvatar(user.getAvatar());
        u.setTaskId(taskId);
        u.setApproveFlag(FlowTaskAuditUserApproveFlagEnum.DRAFT.getValue());
        return u;
    }

    /**
     * 启动工作流
     * @param flowApply 申请
     */
    @Transactional
    public String startFlow(FlowApply flowApply) {

        LocalDateTime now = LocalDateTime.now();
        // 通过申请类型，获取审批流程配置
        AuditProcess config = auditConfigService.selectByOrderType(flowApply.getApplyType());


        // 获得申请的ID
        String applyId = flowApply.getId();
        // 清除掉旧的流程信息
        clearFlowByApplyId(applyId);

        Flow flow = new Flow();
        // 生成ID
        String id = idGenerator.next()+"";
        flow.setId(id);
        // 工作流名称 为 配置名称 + 当前时间
        flow.setName(config.getConfigName()+ DateUtil.format(now, "yyyyMMddHHmmss"));
        flow.setApplyId(applyId);
        flow.setApplyNumber(flowApply.getNumber());
        flow.setApplyType(flowApply.getApplyType());
        flow.setApplyTime(flowApply.getCreateTime());
        flow.setState(ApplyStateEnum.APPROVING.getValue());
        flowMapper.insert(flow);


        CommonUserDTO startUser = new CommonUserDTO();
        // 申请人ID
        Long flowApplyCreateUserId = flowApply.getCreateUserId();
        startUser.setUserId(flowApplyCreateUserId);
        startUser.setNickName(sysUserService.getUserNameById(flowApplyCreateUserId));
        startUser.setAvatar(sysUserService.getUserAvatarById(flowApplyCreateUserId));

        // 任务列表
        List<FlowTask> taskList = new ArrayList<>();

        // 发起人节点
        AuditConfigNode startNode = new AuditConfigNode();
        startNode.setName("发起人");
        List<CommonUserDTO> startNodeAuditUser = new ArrayList<>();
        startNodeAuditUser.add(startUser);
        startNode.setAuditUserList(startNodeAuditUser);

        // 发起人任务
        FlowTask startTask = createTask(startNode, flow.getId(), flowApply, config.getTodoType());
        startTask.setTaskType(FlowTaskTypeEnum.DRAFT.getValue());
        startTask.setEndTime(now);
        startTask.setState(FlowTaskStateEnum.COMPLETE.getValue());
        taskList.add(startTask);

        // 审批节点
        List<AuditConfigNode> nodes = auditConfigService.getNodes(config.getId());
        for (AuditConfigNode node : nodes){
            String auditUser = node.getAuditUser();
            List<CommonUserDTO> auditUserList = parseAuditUser(auditUser);
            node.setAuditUserList(auditUserList);
            // 取出最后一个任务
            FlowTask flowTask = createTask(node, flow.getId(), flowApply, config.getTodoType());
            flowTask.setTaskType(node.getTaskType());
            flowTask.setAuditRule(node.getAuditRule());
            taskList.add(flowTask);
        }

        List<FlowTaskAuditUser> taskAuditUserList = new ArrayList<>();
        for(int i = 0 ; i < taskList.size() ; i++){
            FlowTask task = taskList.get(i);
            List<FlowTaskAuditUser> auditUser = task.getAuditUser();
            taskAuditUserList.addAll(auditUser);
            if (i != taskList.size() - 1) {
                FlowTask taskNext = taskList.get(i+1);
                task.setNextTaskId(taskNext.getId());
            }
        }

        flowTaskAuditUserMapper.insertBatch(taskAuditUserList);
        flowTaskMapper.insertBatch(taskList);

        // 完成开始任务，如果是无需审核的直接通过
        completeTask(startTask.getId(), null);

        return id;

    }

    public FlowTask getTaskById(String taskId) {
        FlowTask task = flowTaskMapper.selectById(taskId);
        task.setAuditUser(getAuditUserByTaskId(taskId));
        return task;
    }

    @Transactional
    public void doTask(DoTaskDTO doTaskDTO) {
        String taskId = doTaskDTO.getTaskId();
        String remark = doTaskDTO.getRemark();
        if (doTaskDTO.isPass()) {
            completeTask(taskId, remark);
        } else {
            rejectTask(taskId, remark);
        }
    }

    public Todo createTodo(FlowTask task, Long userId) {
        String applyId = task.getApplyId();
        String applyNumber = task.getApplyNumber();
        FlowApply flowApply = flowApplyMapper.selectById(applyId);

        Integer todoType = task.getTodoType();
        Todo todo = new Todo();
        todo.setTaskId(task.getId());
        todo.setApplyId(applyId);
        todo.setApplyType(flowApply.getApplyType());
        todo.setApplyTypeName(flowApply.getApplyTypeName());
        todo.setApplyNumber(applyNumber);
        todo.setApplyTime(flowApply.getCreateTime());
        todo.setUserId(userId);

        // 申请人ID
        Long applyUserId = flowApply.getCreateUserId();
        todo.setApplyUserId(applyUserId);
        SysUserDetailDTO applyUserDetail = sysUserService.getSysUserDetailById(applyUserId);
        todo.setApplyUserName(applyUserDetail.getNickName());
        todo.setApplyCompanyId(applyUserDetail.getCompanyId());
        todo.setApplyDeptId(applyUserDetail.getDeptId());
        Company userCompany = applyUserDetail.getCompany();
        SysDept userDept = applyUserDetail.getDept();
        if (userCompany != null) {
            todo.setApplyCompanyName(userCompany.getName());
        }
        if (userDept != null) {
            todo.setApplyDeptName(userDept.getDeptName());
        }
        todo.setTodoType(task.getTodoType());
        todo.setTodoType(todoType);
        return todo;
    }

    /**
     * 增加任务的待办信息
     * @param task 任务
     */
    public void addTodo(FlowTask task) {

        List<Todo> list = new ArrayList<>();
        List<FlowTaskAuditUser> auditUser = task.getAuditUser();

        if (auditUser != null && !auditUser.isEmpty()) {
            for (FlowTaskAuditUser commonUserDTO : auditUser) {
                list.add(createTodo(task, commonUserDTO.getUserId()));
            }
        }
        todoMapper.insertBatch(list);
    }

    /**
     * 完成当前的任务
     */
    private void completeCurrentTask(FlowTask task, String remark) {

        FlowTaskAuditUser auditUser = task.findAuditUser(SecurityUtils.getUserId());
        if (auditUser == null) {
            throw new BusinessException("当前用户不是任务的审批人");
        }
        LocalDateTime now = LocalDateTime.now();
        auditUser.setApproveFlag(FlowTaskAuditUserApproveFlagEnum.APPROVE.getValue());
        auditUser.setApproveTime(now);

        LambdaUpdateWrapper<FlowTaskAuditUser> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(FlowTaskAuditUser::getApproveFlag, FlowTaskAuditUserApproveFlagEnum.APPROVE.getValue());
        updateWrapper.set(FlowTaskAuditUser::getApproveTime, now);
        updateWrapper.eq(FlowTaskAuditUser::getId, auditUser.getId());
        flowTaskAuditUserMapper.update(null, updateWrapper);

        FlowTaskApprove approve = new FlowTaskApprove();

        approve.setUserId(auditUser.getUserId());
        approve.setUserName(auditUser.getUserName());
        approve.setTaskId(task.getId());
        approve.setRemark(remark);
        approve.setOperateTime(LocalDateTime.now());
        flowTaskApproveMapper.insert(approve);

    }

    /**
     * 检查任务是否完成
     */
    private boolean checkTaskFinish(FlowTask task) {

        if (task.getTaskType() == FlowTaskTypeEnum.APPROVE_SINGLE.getValue()) {
            return true;
        } else if (task.getTaskType() == FlowTaskTypeEnum.APPROVE_MULTI.getValue()) {
            List<FlowTaskAuditUser> auditUser = task.getAuditUser();
            boolean finishFlag;
            // 如果是或签，和单人审批的流程一致
            if (task.getAuditRule() == 1) {
                finishFlag = auditUser.stream().anyMatch(item->item.getApproveFlag() == FlowTaskAuditUserApproveFlagEnum.APPROVE.getValue());
            } else if (task.getAuditRule() == 2) {
                finishFlag = auditUser.stream().allMatch(item->item.getApproveFlag() == FlowTaskAuditUserApproveFlagEnum.APPROVE.getValue());
            } else {
                throw new BusinessException("无效的任务审批规则");
            }
            return finishFlag;
        } else {
            throw new BusinessException("无效的审批任务类型");
        }
    }



    /**
     * 完成任务
     * @param taskId 完成任务
     */
    @Transactional
    public void completeTask(String taskId, String remark) {
        FlowTask task = getTaskById(taskId);
        boolean taskFinish;
        if (task.getTaskType() == FlowTaskTypeEnum.DRAFT.getValue()) {
            taskFinish = true;
        } else {
            completeCurrentTask(task, remark);
            taskFinish = checkTaskFinish(task);
        }
        // 如果任务还没完成，返回，不执行后续
        if (!taskFinish) {
            return;
        }

        task.setState(FlowTaskStateEnum.COMPLETE.getValue());
        flowTaskMapper.updateById(task);

        // 任务完成的后续工作
        String nextTaskId = task.getNextTaskId();
        Flow flow = flowMapper.selectById(task.getFlowId());
        // 如果没有下一个任务，把工作流改成完成，返回
        if (StringUtils.isBlank(nextTaskId)) {
            flow.setState(FlowStateEnum.APPROVED.getValue());
            flow.setCurrentTaskId("");
            flowMapper.updateById(flow);
            sendApproveEvent(flow);
            return;
        }

        // 否则把下一个任务的状态改成执行中
        FlowTask nextTask = getTaskById(nextTaskId);
        addTodo(nextTask);
        nextTask.setState(FlowTaskStateEnum.RUNNING.getValue());
        flowTaskMapper.updateById(nextTask);
        flow.setCurrentTaskId(nextTaskId);
        flowMapper.updateById(flow);

    }

    /**
     * 驳回任务
     * @param taskId 任务ID
     */
    @Transactional
    public void rejectTask(String taskId, String remark) {
        FlowTask task = getTaskById(taskId);
        task.setState(FlowTaskStateEnum.REJECTED.getValue());
        flowTaskMapper.updateById(task);
        Flow flow = flowMapper.selectById(task.getFlowId());
        flow.setState(FlowStateEnum.REJECT.getValue());
        flowMapper.updateById(flow);
        sendRejectEvent(flow);

    }

    public Flow getById(String flowId) {
        return flowMapper.selectById(flowId);
    }

    public List<FlowTaskAuditUser> getAuditUserByTaskId(String taskId) {
        LambdaQueryWrapper<FlowTaskAuditUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FlowTaskAuditUser::getTaskId, taskId);
        return flowTaskAuditUserMapper.selectList(queryWrapper);
    }

    public List<FlowTaskApprove> getRecordsByTaskId(String taskId) {
        LambdaQueryWrapper<FlowTaskApprove> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FlowTaskApprove::getTaskId, taskId);
        return flowTaskApproveMapper.selectList(queryWrapper);
    }
    
    public List<FlowTask> getTaskList(String flowId) {
        LambdaQueryWrapper<FlowTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(FlowTask::getFlowId, flowId);
        List<FlowTask> list = flowTaskMapper.selectList(queryWrapper);
        for(FlowTask task:list){
            List<FlowTaskAuditUser> auditUser = getAuditUserByTaskId(task.getId());
            task.setAuditUser(auditUser);
            List<FlowTaskApprove> approveList = getRecordsByTaskId(task.getId());
            task.setApproveList(approveList);
        }
        return list;
    }

    /**
     * 发送审批完成的消息
     */
    public void sendApproveEvent(Flow flow) {
        ApplyApproveEvent approveEvent = new ApplyApproveEvent(this, flow);
        eventPublisher.publishEvent(approveEvent);
    }

    public void sendRejectEvent(Flow flow) {
        ApplyRejectEvent approveEvent = new ApplyRejectEvent(this, flow);
        eventPublisher.publishEvent(approveEvent);
    }

}
